/**
* @file hal.cpp
* Hardware abstraction layer.
* It's a abstraction interface to control basic motherboard devices.
*
* Copyrights 2010 Michal Saman, m.saman@designplus.cz.
* This source code is release under the Apache License 2.0.
* http://www.apache.org/licenses/LICENSE-2.0
*/

#include <cpu/cpu.h>
#include <hal/hal.h>
#include <hal/idt.h>
#include <hal/pic.h>
#include <hal/pit.h>
#include <drivers/kbrd/keyboard.h>

/**
* Inicialize HAL.
* Important function that has these competencies:
* Inicialize CPU ( load GTD, load IDT)
* Inicialize Programable Interrupt Controler
* Inicialize Programable Interval Timer and start default counter
* Enable HW interrupts
*/
__attribute__ ((cdecl)) int hal_init() {
	cpu_init();
	pic_init(hal_pic_base0, hal_pic_base1);
	pit_init();
	pit_start_counter(100, PIT_OCW_COUNTER_0, PIT_OCW_MODE_SQUAREWAVEGEN);
	kbrd_driver_install(hal_pic_base0+1);
	enable_interrupts();
	return 0;
}

/**
* Disable HA layer.
* Not much to do now ...
*/
__attribute__ ((cdecl)) int hal_shutdown() {
	cpu_shutdown();
	disable_interrupts();
	return 0;
}

/**
* Function generate interrupt by given number.
* @param int n Number whitch interrupt to generate (int n)
*/
__attribute__ ((cdecl)) void generate_interrupt(int n) {
	__asm__ volatile (
			"movb %0, genint+1\n\t"
			"jmp genint\n\t"
			"genint:\n\t"
			"	int $0"			// value of interrupt number we set above
			:
			: "r"(n) );
}

/**
* Function sends PIC command EndOfInterrupt.
* @param t	Number of HW interrupt
*/
void end_hw_interrupt(uint32_t n) {
	if (n>16)
		return;
	// check if int is for second PIC
	if (n>=8)
		pic_send_command(PIC_OCW2_MASK_EOI, 1);
	// always send EOI to master PIC
	pic_send_command(PIC_OCW2_MASK_EOI, 0);
}

/**
* Set functoion handler for specified interrupt.
* @param n			Interrupt number.
* @param &fn		Address of function.
*/
__attribute__ ((cdecl)) void set_handler_interrupt(uint32_t n, void (*fn) () ) {
	idt_set_descriptor(n, IDT_DESC_PRESENT | IDT_DESC_BIT32, 0x8, fn);
}

/**
* Read data (one byte) from specified port address.
* @param portid		Port addess from we want reads.
* @return Data readed from specified port.
*/
__attribute__ ((cdecl)) uint8_t inportb(uint16_t portid) {

	uint8_t ret_value;
	__asm__ volatile ("inb %%dx, %%al" : "=a"(ret_value) : "d"(portid) );
	return (uint8_t) ret_value;
}

/**
* Writes data(one byte) to specified port.
* @param portid		Port address that we wants write.
* @param value		Value that we wants write on specified port.
*/
__attribute__ ((cdecl)) void outportb(uint16_t portid, uint8_t value) {
	__asm__ volatile ("outb %%al, %%dx" : : "a"(value), "d"(portid) );
}

/**
* Read data (one word) from specified port address.
* @param portid		Port addess from we want reads.
* @return Data readed from specified port.
*/
__attribute__ ((cdecl)) uint16_t inportw(uint16_t portid) {
	uint16_t ret_value;
	__asm__ volatile ("inw %%dx, %%ax" : "=a"(ret_value) : "d"(portid) );
	return (uint16_t) ret_value;
}

/**
* Writes data (one word) to specified port.
* @param portid		Port address that we wants write.
* @param value		Value that we wants write on specified port.
*/
__attribute__ ((cdecl)) void outportw(uint16_t portid, uint16_t value) {
	__asm__ volatile ("outw %%ax, %%dx" : : "a"(value), "d"(portid) );
}

/**
* Read data (double word) from specified port address.
* @param portid		Port addess from we want reads.
* @return Data readed from specified port.
*/
__attribute__ ((cdecl)) uint32_t inportdw(uint16_t portid) {
	uint32_t ret_value;
	__asm__ volatile ("inl %%dx, %%eax" : "=a"(ret_value) : "d"(portid) );
	return (uint32_t) ret_value;
}

/**
* Writes data (double word) to specified port.
* @param portid		Port address that we wants write.
* @param value		Value that we wants write on specified port.
*/
__attribute__ ((cdecl)) void outportdw(uint16_t portid, uint32_t value) {
	__asm__ volatile ("outl %%eax, %%dx" : : "a"(value), "d"(portid) );
}

/**
 * String version of inportw.
 * Copy sequence of words from IO port to the memory buffer.
 * @param portid		Port address that we wants read.
 * @param buffer		Memory buffer where the values will be copiied.
 * @param buffer		Count of words to transfer from port to buffer.
 */
void inportsw(uint16_t portid, void * buffer, size_t count) {
	__asm__ volatile ("rep; insw" : "=D"(buffer), "=c"(count) : "d"(portid), "0"(buffer), "1"(count));
}

/**
 * String version of outportw.
 * Copy sequence of words from memory buffer to IO port.
 * @param portid		Port address that we wants write.
 * @param buffer		Memory buffer of data that we wants transfer to IO port.
 * @param count			Count of words to transfer from buffer to port.
 */
void outportsw(uint16_t portid, void * buffer, size_t count) {
	__asm__ volatile ("rep; outsw" : "=S"(buffer), "=c"(count) : "d"(portid), "0"(buffer), "1"(count));
}

